/*
Authors: Eli Yucht 300388949 and Lior Fass 300069705
Project: Hamming
Description: Hamming Encoder Decoder Class implementation
*/
#include "reciever.h"

Reciever::Reciever()
{
	file_p = NULL;
	rcvr_socket = INVALID_SOCKET;
	nRecivedBytes = 0;
	nReconstructedBytes = 0;
	nErrors = 0;
}

Reciever::~Reciever()
{
	if (file_p != NULL)
		fclose(file_p);
	closesocket(rcvr_socket);
	WSACleanup();
};

int Reciever::Init(int argc, char *argv[])
{
	int status = Check_Arguments(argc,argv);
	if (status)
	{
		cout << "args Check Failed" << endl;
		return 1;
	}

	file_p = fopen(argv[3], "wb");
	if (file_p == NULL)
	{
		cerr << "Output File open failed\n";
		return 1;
	}

	rcvr_socket = Socket_tool::Connect_Channel(argv[1], (unsigned short)atoi(argv[2]));
	if (rcvr_socket == INVALID_SOCKET)
	{
		cerr << "Socket opening failed" << endl;
		return 1;
	}

	return 0; // Great success obj initialized
}

int Reciever::Check_Arguments(int argc, char *argv[])
{
	if (argc != NUM_OF_ARGS + 1)
	{
		cerr << "Invalid number of arguments for receiver\n";
		return 1;
	}
	unsigned long ip_addr = inet_addr(argv[1]);

	if (ip_addr == INADDR_NONE)
	{
		cerr << "Invalid ip address\n";
		return 1;
	}

	if ((atoi(argv[2]) < 0) | (atoi(argv[2]) > 65536))
	{
		cerr << "Invalid port number (should be a number between 0-65536)\n";
		return 1;
	}

	return 0; // Arg Passed
}

int Reciever::Run()
{
	int status = 0;


	status = Recieve_Decode_WriteFile();
	if (status)
	{
		cout << "send result to sender failed" << endl;
		return 1;
	}

	status = PrintAndSendResult();
	if (status)
	{
		cout << "send result to sender failed" << endl;
		return 1;
	}

	status = shutdown(rcvr_socket, SD_SEND);
	if (status == SOCKET_ERROR)
	{
		cerr << "shutdown for receiver socket failed: " << WSAGetLastError() << "\n";
		return 1;
	}

	return 0;
}

int Reciever::Recieve_Decode_WriteFile()
{
	int bytesRemaining;
	size_t countW2F = 0, elements_read = 0;
	TransferResult_t socketResult = TransferResult_t::TRNS_FAILED;
	unsigned char bytes_rcvd[31];
	unsigned char res_hamming_blocks[32];
	unsigned char res_buffer_bytes[26];

	while (socketResult != TransferResult_t::TRNS_DISCONNECTED)
	{
		socketResult = Socket_tool::ReceiveBuffer((char*)bytes_rcvd, 31, &bytesRemaining, rcvr_socket);
		nRecivedBytes += 31 - bytesRemaining;
		if (socketResult == TransferResult_t::TRNS_FAILED)
		{
			cerr << "Socket Failed" << endl;
			return 1;
		}
		else if (socketResult == TransferResult_t::TRNS_DISCONNECTED)
		{
			if (bytesRemaining == 15) // we have reached the last 16 bytes in the socket => the last 13 bytes in the file
			{
				Bytes_Man::Bytes_to_Block31(res_hamming_blocks, bytes_rcvd, 4);
				nErrors += Hamming_EDM::decode_Blocks(res_hamming_blocks, 4);
				Bytes_Man::bits_26_to_Byte(res_hamming_blocks, res_buffer_bytes);
				countW2F = fwrite(res_buffer_bytes, 1, 13, file_p);
				if (countW2F != 13)
				{
					cerr << "File write failed at EOF\n";
					return 1;
				}
				else
					nReconstructedBytes += countW2F;
			}
			else if (bytesRemaining != 31)
			{
				cerr << "Socket disconnected unexpectedly" << endl;
				return 1;
			}
		}
		else if (socketResult == TransferResult_t::TRNS_SUCCEEDED) // guarantee 31 Bytes
		{
			Bytes_Man::Bytes_to_Block31(res_hamming_blocks, bytes_rcvd, 8);
			nErrors += Hamming_EDM::decode_Blocks(res_hamming_blocks, 8);
			Bytes_Man::bits_26_to_Byte(res_hamming_blocks, res_buffer_bytes);
			Bytes_Man::bits_26_to_Byte(res_hamming_blocks + 16, res_buffer_bytes + 13);
			countW2F = fwrite(res_buffer_bytes, 1, 26, file_p);
			if (countW2F != 26)
			{
				cerr << "File write failed\n";
				return 1;
			}
			else
				nReconstructedBytes += countW2F;
		}
	}
	return 0;
}

int Reciever::PrintAndSendResult()
{
	int status;
	char str[128]; //more then enough to represent 3 ints
	sprintf(str, "recieved:       %d bytes\nreconstructed:  %d bytes\ncorrected:      %d errors\n", nRecivedBytes, nReconstructedBytes, nErrors);
	cerr << str;
	status = Socket_tool::SendString(str, rcvr_socket);
	if (status != TransferResult_t::TRNS_SUCCEEDED)
	{
		cout << "transmission of result string failed " << WSAGetLastError() << endl;
		return 1;
	}
	return 0;
}